﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.IO;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace Map_Normalizer
{
    public partial class MainForm : Form
    {
        // Data Members
        MapDiag mdMap;

        // Static settings to date.
        private readonly uint MaxMapHeight_Regular = 200;
        private readonly uint MaxMapWidth_Regular = 208;

        private readonly uint MaxMapHeight_SS = 200;
        private readonly uint MaxMapWidth_SS = 208;

        private readonly uint MaxMapHeight_GP = 300;
        private readonly uint MaxMapWidth_GP = 300;

        // Main Form initialization.
        public MainForm()
        {
            InitializeComponent();
        }

        // Check if map dimensions are in range.
        private bool MapInRange(uint width, uint height)
        {
            width *= 2; // We'll be getting server-side values.
            if (cbGroup.Checked)
                return (width <= MaxMapWidth_GP && height <= MaxMapHeight_GP);
            if (cbSponsor.Checked)
                return (width <= MaxMapWidth_SS && height <= MaxMapHeight_SS);
            return (width <= MaxMapWidth_Regular && height <= MaxMapHeight_Regular);
        }

        // Activity Log Functions
        private void btnClearLog_Click(object sender, EventArgs e)
        {
            ClearLog();
        }

        private void btnSaveLog_Click(object sender, EventArgs e)
        {
            if (sfdLogFile.ShowDialog() == DialogResult.OK)
            {
                SaveLog(sfdLogFile.FileName);
            }
        }

        private void AppendLog()
        {
            AppendLog("");
        }

        private void AppendLog(string text)
        {
            tbActivityLog.Text += text + Environment.NewLine;
            if (tbActivityLog.Lines.Length > 10)
            {
                tbActivityLog.Focus();
                tbActivityLog.Select(tbActivityLog.Text.Length - 1, 0);
                tbActivityLog.ScrollToCaret();
            }
        }

        private void ClearLog()
        {
            tbActivityLog.Text = "";
        }

        private void SaveLog(string filename)
        {
            try
            {
                StreamWriter sw = new StreamWriter(filename, false);
                sw.Write(tbActivityLog.Text);
                sw.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        // Map File Area Functions
        private void btnBrowse_Click(object sender, EventArgs e)
        {
            if (ofdMapFile.ShowDialog() == DialogResult.OK)
            {
                tbMapFile.Text = ofdMapFile.FileName;
            }
        }

        private void btnScan_Click(object sender, EventArgs e)
        {
            ClearLog();
            grpScanResults.Enabled = ScanMap(tbMapFile.Text);
        }

        private void btnFixOOR_Click(object sender, EventArgs e)
        {
            RepairMap(tbMapFile.Text);
        }

        private void nLastObjectId_Focus(object sender, EventArgs e)
        {
            DoSelect(nLastObjectId);
        }

        private void nLastFloorId_Focus(object sender, EventArgs e)
        {
            DoSelect(nLastFloorId);
        }

        private void nLastWallId_Focus(object sender, EventArgs e)
        {
            DoSelect(nLastWallId);
        }

        private void DoSelect(System.Windows.Forms.NumericUpDown obj)
        {
            obj.Select(0, obj.Value.ToString().Length);
        }

        // Scanning & Repairs
        private bool ScanMap(string filename)
        {
            AppendLog("Begin scan of map file: " + filename);
            bool success = true;
            try
            {
                mdMap = new MapDiag();
                mdMap.LoadMap(filename);
                AppendLog("Map loaded successfully!");
                AppendLog();

                // Check dimensions.
                txtMapInRange.Text = "Yes";
                if (!MapInRange(mdMap.Width, mdMap.Height))
                {
                    txtMapInRange.Text = "NO";
                    AppendLog(
                        String.Format("WARNING: Map dimensions ({0}x{1}) might be out of range for current mode!",
                            mdMap.Width,
                            mdMap.Height)
                        );
                }

                // Run through all the elements and check if any are OOR.
                uint oorObjects = 0;
                uint oorFloors = 0;
                uint oorWalls = 0;

                ushort nLastObject = (ushort)nLastObjectId.Value;
                ushort nLastFloor = (ushort)nLastFloorId.Value;
                ushort nLastWall = (ushort)nLastWallId.Value;


                AppendLog("Checking elements inside the map...");
                AppendLog(String.Format("   - Last Obj:   {0}", nLastObject));
                AppendLog(String.Format("   - Last Floor: {0}", nLastFloor));
                AppendLog(String.Format("   - Last Wall:  {0}", nLastWall));
                AppendLog();
                for (uint y = 0; y < mdMap.Height; y++)
                {
                    for (uint x = 0; x < mdMap.Width; x++)
                    {
                        Tile t = mdMap.GetTile(x, y);

                        if (t.Object > nLastObject)
                        {
                            AppendLog(String.Format("WARNING: Object {0} at ({1},{2})", t.Object, x * 2, y));
                            oorObjects++;
                        }
                        if (t.Floor > nLastFloor)
                        {
                            AppendLog(String.Format("WARNING: Floor {0} at ({1},{2})", t.Floor, x * 2, y));
                            oorFloors++;
                        }
                        if (t.WallNW > nLastWall)
                        {
                            AppendLog(String.Format("WARNING: Wall (NW) {0} at ({1},{2})", t.WallNW, x * 2, y));
                            oorWalls++;
                        }
                        if (t.WallNE > nLastWall)
                        {
                            AppendLog(String.Format("WARNING: Wall (NE) {0} at ({1},{2})", t.WallNE, x * 2, y));
                            oorWalls++;
                        }
                    }
                }

                AppendLog("Scan completed!");
                AppendLog();
                AppendLog("  - OOR Objects: " + oorObjects.ToString());
                AppendLog("  - OOR Floors:  " + oorFloors.ToString());
                AppendLog("  - OOR Walls:   " + oorWalls.ToString());

                // Set results.
                txtObjectsOOR.Text = oorObjects.ToString();
                txtFloorsOOR.Text = oorFloors.ToString();
                txtWallsOOR.Text = oorWalls.ToString();
            }
            catch (Exception ex)
            {
                AppendLog("ERROR: " + ex.Message);
                MessageBox.Show(ex.Message, "Error Loading Map", MessageBoxButtons.OK, MessageBoxIcon.Error);
                success = false;
            }
            return success;
        }

        private bool RepairMap(string filename)
        {
            string backup_filename = filename + ".bak";
            ushort n = 0;
            bool success = true;

            // Make sure we're creating a new file and not overwriting one!
            while (File.Exists(backup_filename))
            {
                backup_filename = filename + ".bak" + n.ToString();
                n++;
            }

            try
            {
                AppendLog("Creating backup copy: " + backup_filename);
                File.Copy(filename, backup_filename);

                AppendLog("Begin repair of map: " + filename);
                // Run through all the elements and check if any are OOR.
                ushort nLastObject = (ushort)nLastObjectId.Value;
                ushort nLastFloor = (ushort)nLastFloorId.Value;
                ushort nLastWall = (ushort)nLastWallId.Value;

                AppendLog("Checking elements inside the map...");
                AppendLog(String.Format("   - Last Obj:   {0}", nLastObject));
                AppendLog(String.Format("   - Last Floor: {0}", nLastFloor));
                AppendLog(String.Format("   - Last Wall:  {0}", nLastWall));
                AppendLog();
                for (uint y = 0; y < mdMap.Height; y++)
                {
                    for (uint x = 0; x < mdMap.Width; x++)
                    {
                        Tile t = mdMap.GetTile(x, y);

                        if (t.Object > nLastObject)
                        {
                            AppendLog(String.Format("RESET: Object {0} at ({1},{2})", t.Object, x*2, y));
                            mdMap.SetObject(x,y,0);
                        }
                        if (t.Floor > nLastFloor)
                        {
                            AppendLog(String.Format("RESET: Floor {0} at ({1},{2})", t.Floor, x*2, y));
                            mdMap.SetFloor(x, y, 0);
                        }
                        if (t.WallNW > nLastWall)
                        {
                            AppendLog(String.Format("RESET: Wall (NW) {0} at ({1},{2})", t.WallNW, x*2, y));
                            mdMap.SetWallNw(x, y, 0);
                        }
                        if (t.WallNE > nLastWall)
                        {
                            AppendLog(String.Format("RESET: Wall (NE) {0} at ({1},{2})", t.WallNE, x*2, y));
                            mdMap.SetWallNe(x, y, 0);
                        }
                    }
                }

                AppendLog("Sweep completed!");
                AppendLog("Writing file to disk...");
                mdMap.SaveMap(filename);
                AppendLog();
                AppendLog("Operation completed!");
            }
            catch (Exception ex)
            {
                success = false;
                AppendLog("ERROR: " + ex.Message);
                MessageBox.Show(ex.Message, "Error Repairing Map", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            // Restore backup file if it was successfully made, but we had
            // errors writing it. We might've messed the new one.
            if (!success && File.Exists(backup_filename))
            {
                File.Delete(filename);
                File.Move(backup_filename, filename);
            }

            return success;
        }

        private void lnkProjectPage_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            System.Diagnostics.Process.Start(@"http://www.icerealm.org/furcadia/mapnormalizer");
        }
    }
}
